/*
Copyright [2020] [https://www.xiaonuo.vip]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：

1.请不要删除和修改根目录下的LICENSE文件。
2.请不要删除和修改Snowy源码头部的版权声明。
3.请保留源码和相关描述文件的项目出处，作者声明等。
4.分发源码时候，请注明软件出处 https://gitee.com/xiaonuobase/snowy
5.在修改包名，模块名称，项目代码等时，请注明软件出处 https://gitee.com/xiaonuobase/snowy
6.若您的项目无法满足以上几点，可申请商业授权，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package vip.xiaonuo.modular.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.xiaonuo.core.consts.CommonConstant;
import vip.xiaonuo.core.context.constant.ConstantContextHolder;
import vip.xiaonuo.core.enums.CommonStatusEnum;
import vip.xiaonuo.core.exception.AuthException;
import vip.xiaonuo.core.exception.ServiceException;
import vip.xiaonuo.core.exception.enums.AuthExceptionEnum;
import vip.xiaonuo.core.factory.PageFactory;
import vip.xiaonuo.core.pojo.login.SysLoginUser;
import vip.xiaonuo.core.pojo.page.PageResult;
import vip.xiaonuo.core.util.MD5Util;
import vip.xiaonuo.core.util.PoiUtil;
import vip.xiaonuo.core.util.StrUtils;
import vip.xiaonuo.modular.redis.RedisService;
import vip.xiaonuo.modular.user.entity.User;
import vip.xiaonuo.modular.user.enums.UserExceptionEnum;
import vip.xiaonuo.modular.user.mapper.UserMapper;
import vip.xiaonuo.modular.user.param.RegisterUserInfo;
import vip.xiaonuo.modular.user.param.UserInvite;
import vip.xiaonuo.modular.user.param.UserParam;
import vip.xiaonuo.modular.user.param.WxUserRegister;
import vip.xiaonuo.modular.user.service.UserService;
import vip.xiaonuo.modular.user.util.SmsUtils;
import vip.xiaonuo.sys.core.cache.UserCache;
import vip.xiaonuo.sys.core.enums.LogSuccessStatusEnum;
import vip.xiaonuo.sys.core.jwt.JwtPayLoad;
import vip.xiaonuo.sys.core.jwt.JwtTokenUtil;
import vip.xiaonuo.sys.core.log.LogManager;

import java.math.BigDecimal;
import java.util.*;


/**
 * 用户信息service接口实现类
 *
 * @author wanglei
 * @date 2021-08-25 18:59:23
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private RedisService redisService;

    @Autowired
    private UserCache userCache;

    @Autowired
    private UserMapper userMapper;


    @Override
    public PageResult<User> page(UserParam userParam) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(userParam)) {

            // 根据账号 查询
            if (ObjectUtil.isNotEmpty(userParam.getAccount())) {
                queryWrapper.lambda().eq(User::getAccount, userParam.getAccount());
            }
            // 根据电话 查询
            if (ObjectUtil.isNotEmpty(userParam.getPhone())) {
                queryWrapper.lambda().eq(User::getPhone, userParam.getPhone());
            }
            // 根据地址 查询
            if (ObjectUtil.isNotEmpty(userParam.getAddress())) {
                queryWrapper.lambda().eq(User::getAddress, userParam.getAddress());
            }
            // 根据年龄 查询
            if (ObjectUtil.isNotEmpty(userParam.getAge())) {
                queryWrapper.lambda().eq(User::getAge, userParam.getAge());
            }
            // 根据性别 查询
            if (ObjectUtil.isNotEmpty(userParam.getSex())) {
                queryWrapper.lambda().eq(User::getSex, userParam.getSex());
            }
            // 根据收入 查询
            if (ObjectUtil.isNotEmpty(userParam.getIncome())) {
                queryWrapper.lambda().eq(User::getIncome, userParam.getIncome());
            }
            // 根据行业 查询
            if (ObjectUtil.isNotEmpty(userParam.getIndustry())) {
                queryWrapper.lambda().eq(User::getIndustry, userParam.getIndustry());
            }
            // 根据昵称 查询
            if (ObjectUtil.isNotEmpty(userParam.getNickname())) {
                queryWrapper.lambda().eq(User::getNickname, userParam.getNickname());
            }
            // 根据账户状态(0-正常，1-停用，2-删除) 查询
            if (ObjectUtil.isNotEmpty(userParam.getStatus())) {
                queryWrapper.lambda().eq(User::getStatus, userParam.getStatus());
            }
            // 根据邀请码 查询
            if (ObjectUtil.isNotEmpty(userParam.getInvitationCode())) {
                queryWrapper.lambda().eq(User::getInvitationCode, userParam.getInvitationCode());
            }
            // 根据邀请人 查询
            if (ObjectUtil.isNotEmpty(userParam.getInviter())) {
                queryWrapper.lambda().eq(User::getInviter, userParam.getInviter());
            }
            // 根据可提现余额 查询
            if (ObjectUtil.isNotEmpty(userParam.getWithdrawOver())) {
                queryWrapper.lambda().eq(User::getWithdrawOver, userParam.getWithdrawOver());
            }
            // 根据出生年月 查询
            if (ObjectUtil.isNotEmpty(userParam.getInitialDate())) {
                queryWrapper.lambda().eq(User::getInitialDate, userParam.getInitialDate());
            }
        }
        return new PageResult<>(this.page(PageFactory.defaultPage(), queryWrapper));
    }

    @Override
    public List<User> list(UserParam userParam) {
        return this.list();
    }

    @Override
    public Map add(UserParam userParam) {
        User userByPhone = this.getUserByPhone(userParam.getPhone());
        if (ObjectUtil.isNotEmpty(userByPhone))
            throw new ServiceException(HttpStatus.HTTP_INTERNAL_ERROR,"当前用户已存在");
        User user = new User();
        BeanUtil.copyProperties(userParam, user,"inviter","withdrawOver");
        user.setWithdrawOver(BigDecimal.ZERO);
        user.setAccount(userParam.getPhone());
        user.setSex(0);
        user.setPassword(MD5Util.MD5Encode(UUID.randomUUID().toString(),"utf-8"));
        user.setStatus(CommonConstant.NORMAL);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        this.save(user);
        return login(userParam.getPhone());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<UserParam> userParamList) {
        userParamList.forEach(userParam -> {
            this.removeById(userParam.getId());
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(UserParam userParam) {
        User user = this.queryUser(userParam);
        BeanUtil.copyProperties(userParam, user,"inviter","phone","withdrawOver");
        user.setUpdateTime(new Date());
        this.updateById(user);
    }

    @Override
    public User detail(UserParam userParam) {
        return this.queryUser(userParam);
    }

    /**
     * 获取用户信息
     *
     * @author wanglei
     * @date 2021-08-25 18:59:23
     */
    private User queryUser(UserParam userParam) {
        User user = this.getById(userParam.getId());
        if (ObjectUtil.isNull(user)) {
            throw new ServiceException(UserExceptionEnum.NOT_EXIST);
        }
        return user;
    }

    @Override
    public void export(UserParam userParam) {
        List<User> list = this.list(userParam);
        PoiUtil.exportExcelWithStream("SnowyUser.xls", User.class, list);
    }

    /**
     * 发送短信验证码
     * @param phone
     */
    @Override
    public void sendSmsCode(String phone) {
        if (StringUtils.isBlank(phone))
            throw new ServiceException(HttpStatus.HTTP_INTERNAL_ERROR,"请输入手机号！");
        //从redis中获取验证码信息
        String code = redisService.get(redisService.getCommonKeyPrefix() + phone);
        //验证码不存在
        if (StringUtils.isBlank(code)){
            //获取新的验证码
            code = StrUtils.createRandom(6);
            log.debug("当前短信验证码为："+code);
            //将验证码和当前时间存入redis
            redisService.put(redisService.getCommonKeyPrefix()+phone,code+":"+System.currentTimeMillis(),redisService.getTimeoutSeconds());
            //todo 发送短信
            SmsUtils.sendSms(phone,code);
            return;
        }
         String[] split = code.split(":");
         String oldCode  = split[0];
        log.debug("当前短信验证码为："+oldCode);
         Long registerTime = Long.valueOf(split[1]);
         //验证码申请时间小于一分钟
         if (System.currentTimeMillis() - registerTime < 60 * 1000)
             throw new ServiceException(HttpStatus.HTTP_BAD_REQUEST,"请勿重复申请验证码");
         if (System.currentTimeMillis() - registerTime < 5 * 60 * 1000){
             //存储
             redisService.put(redisService.getCommonKeyPrefix()+phone,oldCode+":"+System.currentTimeMillis(),redisService.getTimeoutSeconds());
             SmsUtils.sendSms(phone,oldCode);
         }

    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map login(Map map) {
        if (StringUtils.isBlank(map.get("phone").toString()) || StringUtils.isBlank(map.get("code").toString()))
            throw new ServiceException(HttpStatus.HTTP_BAD_REQUEST,"验证码或手机号码为空，请检测");
        String phone = map.get("phone").toString();
        String code = map.get("code").toString();
        User user = this.getUserByPhone(phone);
        Map<String,Object> params = new HashMap<>();
        //获取存在redis的验证码
        String registerCode = redisService.get(redisService.getCommonKeyPrefix() + phone);
        if (StringUtils.isBlank(registerCode))
            throw new ServiceException(HttpStatus.HTTP_BAD_REQUEST,"验证码已经过期，请重新申请");
        String[] split = registerCode.split(":");
        //验证码
        String newCode = split[0];
        //code不相等
        if (!code.equals(newCode)) {
            throw new ServiceException(HttpStatus.HTTP_BAD_REQUEST,"验证码不正确");
        }
        if (ObjectUtil.isEmpty(user)){
            //保存用户
            //this.save(getInitUser(registerUserInfo1));
            throw new ServiceException(HttpStatus.HTTP_BAD_METHOD,"该用户未注册");
        }
        //进行登录
        Map token = login(phone);
        redisService.remove(redisService.getCommonKeyPrefix()+phone);
       /* //参数返回前端
        params.put("token",token);
        params.put("userInfo",user);*/
        return token;
    }


    @Override
    public Map login(String phone) {
        if (StringUtils.isBlank(phone))
            throw new ServiceException(HttpStatus.HTTP_BAD_REQUEST,"电话号码不能为空");
        User user = getUserByPhone(phone);
        if (ObjectUtil.isNull(user))
            throw new ServiceException(HttpStatus.HTTP_BAD_METHOD,"用户未注册");
        if (CommonStatusEnum.DISABLE.getCode().equals(user.getStatus())){
            LogManager.me().executeLoginLog(user.getAccount(), LogSuccessStatusEnum.FAIL.getCode(), AuthExceptionEnum.ACCOUNT_FREEZE_ERROR.getMessage());
            throw new AuthException(AuthExceptionEnum.ACCOUNT_FREEZE_ERROR);
        }
        HashMap<String, Object> newHashMap = Maps.newHashMap();
        newHashMap.put("uersInfo",user);
        //构造SysLoginUser
        SysLoginUser sysLoginUser = genSysLoginUser(user);
        //构造jwtPayLoad
        JwtPayLoad jwtPayLoad = new JwtPayLoad(sysLoginUser.getId(), sysLoginUser.getPhone());
        //生成token
        String token = JwtTokenUtil.generateToken(jwtPayLoad);
        //缓存token与登录用户信息对应, 默认2个小时
        this.cacheLoginUser(jwtPayLoad,sysLoginUser);
        //如果开启限制单用户登陆，则踢掉原来的用户
        Boolean enableSingleLogin = ConstantContextHolder.getEnableSingleLogin();
        if (enableSingleLogin) {
            //获取所有的登陆用户
            Map<String, SysLoginUser> allLoginUsers = userCache.getAllKeyValues();
            for (Map.Entry<String, SysLoginUser> loginedUserEntry : allLoginUsers.entrySet()) {
                String loginedUserKey = loginedUserEntry.getKey();
                SysLoginUser loginedUser = loginedUserEntry.getValue();
                //如果账号名称相同，并且redis缓存key和刚刚生成的用户的uuid不一样，则清除以前登录的
                if (loginedUser.getName().equals(sysLoginUser.getName())
                        && !loginedUserKey.equals(jwtPayLoad.getUuid())) {
                    this.clearUser(loginedUserKey, loginedUser.getAccount());
                }
            }
        }
        newHashMap.put("token",token);
        return newHashMap;
    }

    /**
     * 获取分销
     * @param userId
     * @return
     */
    @Override
    public Map<String, Object> invitationList(String userId) {
        User user = this.getByPhone(userId);
        if (ObjectUtil.isNull(user))
            throw new ServiceException(HttpStatus.HTTP_BAD_REQUEST,"用户不存在");
        Map<String,Object> data  = new HashMap<>();
        List<UserInvite> users = userMapper.selectByInvite(user.getId());
        data.put("childs",users);
        data.put("superior",userMapper.selectByIdResultByUserInvite(user.getInviter()));
        return data;
    }

    @Override
    public User searchByOpenId(String openid) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getOpenid,openid);
        return this.getOne(queryWrapper);
    }


    @Override
    public void registerUser(RegisterUserInfo userInfo) {
        User userByPhone = this.getUserByPhone(userInfo.getPhone());
        if (ObjectUtil.isNotEmpty(userByPhone))
            throw new ServiceException(HttpStatus.HTTP_INTERNAL_ERROR,"当前手机已经注册过");
        User user = getUser(userInfo);
        this.save(user);
    }

    @Override
    public void saveFormWeChatInfo(Map map) {
        User user = getUser(map);
        this.save(user);
    }

    @Override
    public String generateInvitationCode(String phone) {
        User user = this.getByPhone(phone);
        if (ObjectUtil.isEmpty(user))
            throw  new ServiceException(HttpStatus.HTTP_NOT_FOUND,"当前用户未注册");
        if (StringUtils.isBlank(user.getInvitationCode())){
            user.setInvitationCode(getInvitationCode());
            userMapper.updateById(user);
        }
        return user.getInvitationCode();
    }

    @Override
    public Map registerByWxUserInfo(WxUserRegister wxUserRegister) {
        HashMap<String, Object> params = Maps.newHashMap();
        User user = this.getUserByPhone(wxUserRegister.getPhoneNumber());
        //判断是否有openId
        if (user!= null && StringUtils.isBlank(user.getOpenid())){
            user.setOpenid(wxUserRegister.getOpenId());
            this.updateById(user);
        }
        //判断openId是否一致
        if (user!= null && !user.getOpenid().equals(wxUserRegister.getOpenId()))
            throw new ServiceException(HttpStatus.HTTP_BAD_REQUEST,"当前手机号码所绑定的微信用户不一致");
        //为空则进行用户数据保存
        if (ObjectUtil.isEmpty(user)){
            user = getUser(wxUserRegister);
            this.save(user);
            user = this.searchByOpenId(user.getOpenid());
        }
        //登录
        Map token = login(user.getPhone());
        return token;
    }

    @Override
    public User getByPhone(String phone) {
        return this.getUserByPhone(phone);
    }

    private User getUser(WxUserRegister userInfo) {
        User user = new User();
        user.setAccount(userInfo.getPhoneNumber());
        user.setPhone(userInfo.getPhoneNumber());
        user.setSex(userInfo.getWxUserInfo().getGender());
        user.setOpenid(userInfo.getOpenId());
        user.setHeadPortrait(userInfo.getWxUserInfo().getAvatarUrl());
        user.setStatus(CommonConstant.NORMAL);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        user.setWithdrawOver(BigDecimal.ZERO);
        user.setPassword(MD5Util.MD5Encode(UUID.randomUUID().toString(),"UTF-8"));
        return user;
    }

    private User getUser(RegisterUserInfo userInfo) {
        User user = new User();
        BeanUtil.copyProperties(userInfo,user);
        user.setAccount(userInfo.getPhone());
        user.setStatus(CommonConstant.NORMAL);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        user.setWithdrawOver(BigDecimal.ZERO);
        user.setPassword(MD5Util.MD5Encode(UUID.randomUUID().toString(),"UTF-8"));
        //获取邀请人信息
        User userByInvitationCode = getUserByInvitationCode(userInfo.getCode());
        if (ObjectUtil.isNotEmpty(userByInvitationCode))
            user.setInviter(userByInvitationCode.getId());
        return user;
    }


    private User getUser(Map jsonObj) {
        User user = new User();
        String openid = jsonObj.get("openid").toString();
        String phone = jsonObj.get("phone").toString();
        user.setOpenid(openid);
        user.setStatus(CommonConstant.NORMAL);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        user.setAccount(phone);
        user.setPhone(phone);
        user.setPassword(UUID.randomUUID().toString().replaceAll("-",""));
        user.setWithdrawOver(BigDecimal.ZERO);
        return user;
    }

    /**
     * 根据手机号码获取当前用户
     * @param phone
     * @return
     */
    private User getUserByPhone(String phone){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getPhone,phone);
        return this.getOne(queryWrapper);
    }

    /**
     * 根据邀请码查询对应的用户信息
     * @param invitationCode
     * @return
     */
    private User getUserByInvitationCode(String invitationCode){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getInvitationCode,invitationCode);
        User user = userMapper.selectOne(queryWrapper);
        return user;
    }


    /**
     * 缓存token与登录用户信息对应, 默认2个小时
     *
     * @author xuyuxiang
     * @date 2020/3/13 14:51
     */
    private void cacheLoginUser(JwtPayLoad jwtPayLoad, SysLoginUser user) {
        String redisLoginUserKey = jwtPayLoad.getUuid();
        userCache.put(redisLoginUserKey, user, Convert.toLong(ConstantContextHolder.getSessionTokenExpireSec()));
    }

    /**
     * 获取登录用户对象
     * @param user
     * @return
     */
    private SysLoginUser genSysLoginUser(User user){
        SysLoginUser sysLoginUser = new SysLoginUser();
        BeanUtil.copyProperties(user, sysLoginUser);
        return sysLoginUser;
    }


    private String getInvitationCode(){
        String code = StrUtils.generateCode();
        if (userMapper.getInvitationCodeCount(code) > 0){
            return getInvitationCode();
        }
        return code;
    }

    /**
     * 根据key清空登陆信息
     *
     * @author xuyuxiang
     * @date 2020/6/19 12:28
     */
    private void clearUser(String loginUserKey, String account) {
        //获取缓存的用户
        Object cacheObject = userCache.get(loginUserKey);

        //如果缓存的用户存在，清除会话，否则表示该会话信息已失效，不执行任何操作
        if (ObjectUtil.isNotEmpty(cacheObject)) {
            //清除登录会话
            userCache.remove(loginUserKey);
            //创建退出登录日志
            LogManager.me().executeExitLog(account);
        }
    }
}
